﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using MongoDB.Driver;
/*
* 代码已托管 https://gitee.com/dlgcy/dotnetcodes
* 版本：2024年1月4日
*/
namespace DotNet.Utilities.MongoDB
{
	public class MongoDbHelper
	{
		protected IMongoClient _client;
		protected IMongoDatabase _db;
		protected bool _isUseTransaction;

		#region 构造

		public MongoDbHelper(IMongoClient client, IMongoDatabase db, bool isUseTransaction)
		{
			_client = client;
			_db = db;
			_isUseTransaction = isUseTransaction;
		}

		public MongoDbHelper() { }

		public void Build(string userName, string password, string ip, int port, string dbName, bool isDirectConnection = true, bool isUseTransaction = false)
		{
			string connectStr = GetConnectStr(userName, password, ip, port, isDirectConnection);
			Build(connectStr, dbName, isUseTransaction);
		}

		public void Build(string connectStr, string dbName, bool isUseTransaction)
		{
			_client = GetMongoClient(connectStr);
			_db = _client.GetDatabase(dbName);
			_isUseTransaction = isUseTransaction;
		}

		/// <summary>
		/// 获取连接字符串
		/// </summary>
		/// <param name="userName">用户名</param>
		/// <param name="password">密码</param>
		/// <param name="ip">IP</param>
		/// <param name="port">端口</param>
		/// <param name="isDirectConnection">是否直接连接</param>
		/// <returns>连接字符串</returns>
		public static string GetConnectStr(string userName, string password, string ip, int port, bool isDirectConnection = true)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append($"mongodb://");
			if (!string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password))
			{
				stringBuilder.Append($"{userName}:{password}@");
			}

			stringBuilder.Append($"{ip}:{port}");
			stringBuilder.Append($"/?directConnection={isDirectConnection}");

			string connectStr = stringBuilder.ToString();
			Console.WriteLine($"ConnectStr：{connectStr.Replace($":{password}", ":<hide>")}");
			return connectStr;
		}

		/// <summary>
		/// 获取一个 MongoDB 客户端实例
		/// </summary>
		/// <param name="connectStr">连接字符串</param>
		/// <returns><see cref="MongoClient"/></returns>
		public static MongoClient GetMongoClient(string connectStr)
		{
			MongoClientSettings settings = MongoClientSettings.FromUrl(new MongoUrl(connectStr));
			settings.ConnectTimeout = TimeSpan.FromSeconds(5);
			settings.ServerSelectionTimeout = TimeSpan.FromSeconds(5);
			settings.MaxConnectionPoolSize = 20;
			var client = new MongoClient(settings);
			return client;
		}

		#endregion

		/// <summary>
		/// 获取供 MongoDB 使用的当前时间
		/// </summary>
		public static DateTime GetDateTimeForMongoDb(DateTime? time = null)
		{
			if (time == null)
			{
				time = DateTime.Now;
			}
			return DateTime.SpecifyKind(time.Value, DateTimeKind.Utc);
		}

		/// <summary>
		/// 创建会话
		/// </summary>
		/// <param name="options">选项</param>
		/// <param name="cancellationToken">取消通知</param>
		/// <returns><see cref="IClientSessionHandle"/></returns>
		public IClientSessionHandle CreateSession(ClientSessionOptions options = null, CancellationToken cancellationToken = default)
		{
			return _client.StartSession(options, cancellationToken);
		}

		/// <summary>
		/// 执行事务
		/// </summary>
		public void ActionWithSession(Action<IClientSessionHandle> action)
		{
			if (!_isUseTransaction)
			{
				throw new Exception("当前配置为不使用事务");
			}

			var session = CreateSession();
			try
			{
				session.StartTransaction();
				action.Invoke(session);
				session.CommitTransaction();
			}
			catch
			{
				session.AbortTransaction();
				throw;
			}
		}

		#region 集合(表)操作

		/// <summary>
		/// 创建集合（表，没有则创建）
		/// </summary>
		/// <param name="name">集合名称</param>
		public void CreateCollection(string name)
		{
			var collectionList = _db.ListCollectionNames().ToList();
			if (collectionList.All(x => x != name))
			{
				_db.CreateCollection(name);
			}
		}

		/// <summary>
		/// 获取所有集合名称
		/// </summary>
		/// <returns>集合名称列表</returns>
		public List<string> GetAllCollectionName()
		{
			return _db.ListCollectionNames().ToList();
		}

		/// <summary>
		/// 通过名称获取集合
		/// </summary>
		/// <typeparam name="T">类型</typeparam>
		/// <param name="name">集合名称</param>
		/// <param name="settings">设置</param>
		/// <returns></returns>
		public IMongoCollection<T> GetCollection<T>(string name, MongoCollectionSettings settings = null)
		{
			return _db.GetCollection<T>(name, settings);
		}

		/// <summary>
		/// 删除集合
		/// </summary>
		/// <param name="name">集合名</param>
		public void DeleteCollection(string name)
		{
			_db.DropCollection(name);
		}

		#endregion

		#region 增删改查

		/// <summary>
		/// 更新或插入实体列表
		/// </summary>
		/// <param name="rows">列表数据</param>
		/// <param name="session"></param>
		public List<T> UpdateOrInsert<T>(List<T> rows, IClientSessionHandle session = null) where T : EntityBase
		{
			string collectionName = typeof(T).Name;
			bool isUseSelfSession = true;
			if (session == null && _isUseTransaction)
			{
				session = CreateSession();
			}
			else
			{
				isUseSelfSession = false;
			}

			try
			{
				if (isUseSelfSession) session.StartTransaction();

				var collection = GetCollection<T>(collectionName);
				rows.ForEach(entity =>
				{
					if (string.IsNullOrWhiteSpace(entity.Id))
					{
						entity.Id = Guid.NewGuid().ToString();
					}

					T entityInDb = _isUseTransaction
						? collection.Find(session, x => x.Id == entity.Id).FirstOrDefault()
						: collection.Find(x => x.Id == entity.Id).FirstOrDefault();
					if (entityInDb == null)
					{
						entity.CreateTime = GetDateTimeForMongoDb();
						if (_isUseTransaction)
						{
							collection.InsertOne(session, entity);
						}
						else
						{
							collection.InsertOne(entity);
						}
					}
					else
					{
						entity.CreateTime = entityInDb.CreateTime;
						entity.UpdateTime = GetDateTimeForMongoDb();
						entity.IsDeleted = false;
						if (_isUseTransaction)
						{
							collection.ReplaceOne(session, x => x.Id == entity.Id, entity);
						}
						else
						{
							collection.ReplaceOne(x => x.Id == entity.Id, entity);
						}
					}
				});

				if (isUseSelfSession) session.CommitTransaction();

				return rows;
			}
			catch (Exception ex)
			{
				Console.WriteLine($"更新或插入数据时异常：{ex}");
				try
				{
					if (isUseSelfSession) session.AbortTransaction();
				}
				catch
				{
					// ignored
				}

				throw;
			}
		}

		/// <summary>
		/// 删除实体列表
		/// </summary>
		/// <param name="rows">列表数据</param>
		/// <param name="isSoftDelete">是否软删除</param>
		/// <param name="session"></param>
		public void Delete<T>(List<T> rows, bool isSoftDelete = true, IClientSessionHandle session = null) where T : EntityBase
		{
			string collectionName = typeof(T).Name;
			bool isUseSelfSession = true;
			if (session == null && _isUseTransaction)
			{
				session = CreateSession();
			}
			else
			{
				isUseSelfSession = false;
			}

			try
			{
				if (isUseSelfSession) session.StartTransaction();

				var collection = GetCollection<T>(collectionName);

				//如果不是软删除，可批量删除;
				if (!isSoftDelete)
				{
					var ids = rows.Select(x => x.Id).ToList();
					if (_isUseTransaction)
					{
						collection.DeleteMany(session, x => ids.Contains(x.Id));
					}
					else
					{
						collection.DeleteMany(x => ids.Contains(x.Id));
					}
					return;
				}

				rows.ForEach(entity =>
				{
					T entityInDb = _isUseTransaction
						? collection.Find(session, x => x.Id == entity.Id).FirstOrDefault()
						: collection.Find(x => x.Id == entity.Id).FirstOrDefault();
					if (entityInDb == null)
					{
						//Ignore
					}
					else
					{
						if (isSoftDelete)
						{
							if (_isUseTransaction)
							{
								collection.UpdateOne(session, x => x.Id == entity.Id,
									Builders<T>.Update.Set(x => x.IsDeleted, true));

								collection.UpdateOne(session, x => x.Id == entity.Id,
									Builders<T>.Update.Set(x => x.UpdateTime,
										GetDateTimeForMongoDb()));
							}
							else
							{
								collection.UpdateOne(x => x.Id == entity.Id,
									Builders<T>.Update.Set(x => x.IsDeleted, true));

								collection.UpdateOne(x => x.Id == entity.Id,
									Builders<T>.Update.Set(x => x.UpdateTime,
										GetDateTimeForMongoDb()));
							}
						}
						else //上面批量删除部分注释掉则此处可生效;
						{
							if (_isUseTransaction)
							{
								collection.DeleteOne(session, x => x.Id == entity.Id);
							}
							else
							{
								collection.DeleteOne(x => x.Id == entity.Id);
							}
						}
					}
				});

				if (isUseSelfSession) session.CommitTransaction();
			}
			catch (Exception ex)
			{
				Console.WriteLine($"删除数据时异常：{ex}");
				try
				{
					if (isUseSelfSession) session.AbortTransaction();
				}
				catch
				{
					// ignored
				}

				throw;
			}
		}

		/// <summary>
		/// 删除实体列表
		/// </summary>
		/// <param name="idList">ID列表</param>
		/// <param name="isSoftDelete">是否软删除</param>
		/// <param name="session"></param>
		public void DeleteByIds<T>(List<string> idList, bool isSoftDelete = true, IClientSessionHandle session = null) where T : EntityBase
		{
			var deleteList = Find<T>(x => idList.Contains(x.Id)).ToList();
			Delete<T>(deleteList, isSoftDelete, session);
		}

		/// <summary>
		/// 查找实体列表
		/// </summary>
		/// <param name="predicate">条件</param>
		/// <returns>实体列表</returns>
		public IQueryable<T> Find<T>(Expression<Func<T, bool>> predicate = null) where T : EntityBase
		{
			string collectionName = typeof(T).Name;
			var datas = GetCollection<T>(collectionName).AsQueryable().Where(x => x.IsDeleted == false);
			if (predicate != null)
			{
				datas = datas.Where(predicate);
			}

			return datas;
		}

		#endregion
	}
}
